<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    .cont{width: 1000px;overflow: hidden;}
    .cont .box{float: left;border: solid 1px black;width: 250px;box-sizing: border-box;text-align: center;}
    .cont .box img{width: 70%;}
    .cont .box p{line-height: 20px;height:40px;overflow: hidden;}

    table img{width:40px;}
    table input[type=number]{width:50px}
  </style>
</head>
<body>
  <div class="cont"></div>
  <br>
  <table border="1" width="800">
    <thead>
      <tr>
        <th>选中</th>
        <th>图片</th>
        <th>名称</th>
        <th>数量</th>
        <th>价格</th>
        <th>小计</th>
        <th>操作</th>
      </tr>
    </thead>
    <tbody>
    </tbody>
    <tfoot>
      <tr>
        <td colspan=2><input type="checkbox" class="allCheck">全选</td>
        <td align="right">总数量：</td>
        <td class="allNumber">0</td>
        <td>总价：</td>
        <td class="allPrice">0</td>
        <td class="removeSelect">删除选中</td>
      </tr>
    </tfoot>
  </table>
</body>
<script>

  var goods = [{
    id:"234u5y6t435",
    src:"https://img10.360buyimg.com/n7/jfs/t1/162258/36/39960/93359/64d60451Fd6112c16/aa411843e00e5287.jpg",
    price:"3999.00",
    name:"荣耀X50 第一代骁龙6芯片 1.5K超清护眼硬核曲屏 5800mAh超耐久大电池 5G 手机 8GB+128GB 雨后初晴"
  },{
    id:"bgsfdg6gc75",
    src:"https://img13.360buyimg.com/n7/jfs/t1/196701/21/35418/52632/64c7500dF4ec2f71d/61c8493f822d1718.jpg",
    price:"899.00",
    name:"Redmi Note 11 5G 天玑810 33W Pro快充 5000mAh大电池 8GB+ 128GB 神秘黑境"
  },{
    id:"789878sdfdtj",
    src:"https://img13.360buyimg.com/n7/jfs/t1/196701/21/35418/52632/64c7500dF4ec2f71d/61c8493f822d1718.jpg",
    price:"1699.00",
    name:"Bigmi Note 11 5G 天玑810 33W Pro快充 5000mAh大电池 8GB+ 128GB 神秘黑境"
  },{
    id:"kjsddtyu",
    src:"https://img10.360buyimg.com/n7/jfs/t1/162258/36/39960/93359/64d60451Fd6112c16/aa411843e00e5287.jpg",
    price:"2666.00",
    name:"P50 第一代骁龙6芯片 1.5K超清护眼硬核曲屏 5800mAh超耐久大电池 5G 手机 8GB+128GB 雨后初晴"
  }];
  const cont = document.querySelector(".cont")
  var str = "";
  for(var i=0;i<goods.length;i++){
    str += `<div class="box">
              <img src="${goods[i].src}" alt="">
              <p>${goods[i].price}</p>
              <p>${goods[i].name}</p>
              <input type="button" value="加入购物车" class="addCart" data-id="${goods[i].id}" data-price="${goods[i].price}" >
            </div>`;
  }
  cont.innerHTML = str;

  // ---------- 加入购物车功能 -------------

  const addCart = document.querySelectorAll(".addCart");
  // 一个购物车中，能存多个商品信息，每个商品之间属于同类数据，数组打包
  // 一个商品的信息有：id，number，每个信息之间不属于同类数据，对象打包

  // const cartData = [{id, number},{id, number}];
  // 测试数据
  // let cartData = [{id:"bgsfdg6gc75", number:2, checked:true, price: 899}];
  // 正式数据
  let cartData = [];

  for(let i=0;i<addCart.length;i++){
    addCart[i].onclick = function(){
      // 获取当前要加入购物车的商品id
      const id = this.dataset.id;
      // 获取当前要加入购物车的商品价格
      const price = this.dataset.price - 0;
      // 在cartData数组内查询是否有对象的id和本次获取到的id重复
      let i=0;
      const flag = cartData.some((val,idx)=>{
        i = idx;
        return val.id === id;
      });
      // 有重复
      if(flag){
        // 当前查到的这个重复对象的number++
        cartData[i].number++
      }else{    // 没查到
        // 说明本次要加入购物车的数据为新数据，push到购物车数组中
        cartData.push({
          id, number: 1, checked:false, price
        })
      }
  
      // 每次添加新商品够，都需要
      // 更新购物车信息（重新渲染购物车信息）
      cartRender();

      // 判断是否需要全选
      allCheck.checked = cartData.every(val=>val.checked);

      // 计算总数量
      allNumber.innerHTML = cartData.reduce( (prev,val)=>prev + val.number , 0 );
      // 根据购物车数据，计算总价
      allPrice.innerHTML = cartData.reduce( (prev,val)=>prev + val.number * val.price , 0 );
    }
  }

  // ---------- 渲染购物车 ----------

  const tbody = document.querySelector("tbody");

  // 初始渲染购物车信息
  cartRender();

  function cartRender(){
    let cartStr = "";
    // 遍历购物车数据，拿到购物车中的商品id
    cartData.forEach(val=>{
      // 遍历总商品数据，用来查询符合购物车中id的商品信息
      goods.forEach(val2=>{
        // 比对两个id
        if(val2.id === val.id){
          // 拼接购物车的页面结构，将对应的数据，放入指定的结构
          cartStr += `<tr data-id="${val.id}">
                        <td><input type="checkbox" ${val.checked ? "checked" : ""} class="check"></td>
                        <td><img src="${val2.src}" alt=""></td>
                        <td>${val2.name}</td>
                        <td><input type="number" value="${val.number}" class="setNum"></td>
                        <td>${val2.price}</td>
                        <td>${ val2.price * val.number }</td>
                        <td class="remove">删除</td>
                      </tr>`;
        }
      })
    })
    // 将拼接后的页面结构放入指定容器
    tbody.innerHTML = cartStr;
  }

  // -------- 全选 --------

  const allCheck = document.querySelector(".allCheck");
  // 根据购物车数据，判断是否需要全选
  allCheck.checked = cartData.every(val=>val.checked);

  // 将所有复选框的事件委托给tbody
  tbody.addEventListener("change", function(eve){
    const e = eve || window.event;
    const target = e.target || e.srcElement;
    if(target.className === "check"){
      // 获取当前正在操作的商品的id
      const id = target.parentNode.parentNode.dataset.id;
      // 根据当前id找到购物车数据中的指定条
      let i=0;
      cartData.some((val,idx)=>{
        i = idx;
        return val.id === id;
      });
      // 改变该数据的选中状态
      cartData[i].checked = target.checked;

      // 根据购物车数据，判断是否需要全选
      allCheck.checked = cartData.every(val=>val.checked);
    }
  })

  // 全选影响单个
  allCheck.onchange = function(){
    // 获取所有单个复选框
    const checkds = tbody.querySelectorAll(".check");
    // 遍历
    checkds.forEach(val=>{
      // 逐个设置选中状态为，当前全选复选框的状态
      val.checked = this.checked;
    })
    // 同步设置，购物车数据中的选中状态
    cartData.forEach(val=>{
      val.checked = this.checked;
    })
  }


  // -------- 总数量 和 总价 --------

  const allNumber = document.querySelector(".allNumber")
  const allPrice = document.querySelector(".allPrice")
  
  // 根据购物车数据，计算总数量
  allNumber.innerHTML = cartData.reduce( (prev,val)=>prev + val.number , 0 );
  // 根据购物车数据，计算总价
  allPrice.innerHTML = cartData.reduce( (prev,val)=>prev + val.number * val.price , 0 );

  // 将所有复选框的事件委托给tbody
  tbody.addEventListener("input", function(eve){
    const e = eve || window.event;
    const target = e.target || e.srcElement;
    if(target.className === "setNum"){
      // 获取当前正在操作的商品的id
      const id = target.parentNode.parentNode.dataset.id;
      // 根据当前id找到购物车数据中的指定条
      let i=0;
      cartData.some((val,idx)=>{
        i = idx;
        return val.id === id;
      });
      // 改变该数据的数量
      cartData[i].number = target.value - 0;

      // 根据购物车数据，计算总数量
      allNumber.innerHTML = cartData.reduce( (prev,val)=>prev + val.number , 0 );
      // 根据购物车数据，计算总价
      allPrice.innerHTML = cartData.reduce( (prev,val)=>prev + val.number * val.price , 0 );
    }
  })


  // ------------ 删除单个 ---------------

  // 单个删除的点击事件
  tbody.addEventListener("click", function(eve){
    const e = eve || window.event;
    const target = e.target || e.srcElement;
    if(target.className === "remove"){
      // 获取要删除的商品的id
      const id = target.parentNode.dataset.id;
      // 根据当前id找到购物车数据中的指定条
      let i=0;
      cartData.some((val,idx)=>{
        i = idx;
        return val.id === id;
      });
      // 删除数据
      cartData.splice(i, 1);
      // 删除当前行的DOM结构
      target.parentNode.remove();

      // 根据购物车数据，计算总数量
      allNumber.innerHTML = cartData.reduce( (prev,val)=>prev + val.number , 0 );
      // 根据购物车数据，计算总价
      allPrice.innerHTML = cartData.reduce( (prev,val)=>prev + val.number * val.price , 0 );
      // 根据购物车数据，判断是否需要全选
      allCheck.checked = cartData.every(val=>val.checked);
    }
  })

  // 删除选中
  const removeSelect = document.querySelector(".removeSelect");

  removeSelect.onclick = function(){
    // 从购物车数据中，过滤出未选中的商品，覆盖原数据（删除选中）
    cartData = cartData.filter(val => !val.checked);
    // 删除指定的DOM
    const checks = tbody.querySelectorAll(".check");
    checks.forEach(val=>{
      if(val.checked === true){
        val.parentNode.parentNode.remove();
      }
    })
    // 根据购物车数据，计算总数量
    allNumber.innerHTML = cartData.reduce( (prev,val)=>prev + val.number , 0 );
    // 根据购物车数据，计算总价
    allPrice.innerHTML = cartData.reduce( (prev,val)=>prev + val.number * val.price , 0 );
  }

  // 修改购物车
  //   全选
  //   删除
  //   计算单个总价
  //   计算全部总价
  //   计算全部总数量

</script>
</html>